{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "toc": true
   },
   "source": [
    "<h1>Table of Contents<span class=\"tocSkip\"></span></h1>\n",
    "<div class=\"toc\"><ul class=\"toc-item\"></ul></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Administration: Validate user profiles\n",
    "\n",
    "* 👟 Ready To Run!\n",
    "* 🗃️ Administration\n",
    "* 👤 User Management\n",
    "\n",
    "__Requirements__\n",
    "* 🔒 Administrator Privileges\n",
    "\n",
    "Some organizations require member profiles to contain values beyond the minumum required attributes necessary to create a valid user in an ArcGIS Online Organization or ArcGIS Enterprise. For instance, in order to comply with policies and regulations, an organization may require a profile picture or a brief description. This notebook will check attribute values for users of the organization to monitor profiles."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To get started, let's import the necessary libraries and connect to our GIS."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import datetime as dt\n",
    "\n",
    "import pandas as pd\n",
    "\n",
    "from arcgis.gis import GIS, ItemProperties, ItemTypeEnum"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "gis = GIS(\"home\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then, let's create a list containing strings that represent all the values that your organization requires (beyond the required attributes for a valid user profile).\n",
    "\n",
    "`access_type` refers to the `User.access` property of a user and it indicates the level of access of the user: private, org, or public.\n",
    "\n",
    "While `access_type` may not be a requirement for your organization to validate a user profile, we have added it here to demonstrate how other useful properties can also be extracted for each user profile to learn more about them. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "complete_profile = ['description', 'thumbnail', 'access', 'access_type']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll define a function that loops through the list we created and inspects a user object for the values of these attributes. We'll create a list of True/False values for each user regarding the necessary attributes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_missing_profile_attrs(member):\n",
    "    non_compliance = []\n",
    "    for attr in complete_profile:\n",
    "        if attr=='access_type':\n",
    "            non_compliance.append(member.access)\n",
    "        else:\n",
    "            if getattr(member, attr) == None:    \n",
    "                non_compliance.append(False)\n",
    "            else:\n",
    "                non_compliance.append(True)\n",
    "    return non_compliance"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can create a list of users in the GIS and loop through them, calling the function written above to inspect each user object. This will create a dictionary with usernames as the _key_ and the list of True/False status for the required attributes as _values_."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "user_profile_status = {}\n",
    "for user in gis.users.search(\"NOT esri_*\"):\n",
    "    missing_profile_atts = get_missing_profile_attrs(user)\n",
    "    user_profile_status[user.username] = missing_profile_atts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The pandas library can be used to create a dataframe from the above dictionary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>description</th>\n",
       "      <th>thumbnail</th>\n",
       "      <th>access</th>\n",
       "      <th>access_type</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>achapkowski_geosaurus</th>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>org</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>amani_geosaurus</th>\n",
       "      <td>True</td>\n",
       "      <td>True</td>\n",
       "      <td>True</td>\n",
       "      <td>public</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>andrew57</th>\n",
       "      <td>True</td>\n",
       "      <td>True</td>\n",
       "      <td>True</td>\n",
       "      <td>private</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>api_data_owner</th>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>org</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>arcgis_python</th>\n",
       "      <td>True</td>\n",
       "      <td>True</td>\n",
       "      <td>True</td>\n",
       "      <td>public</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                      description thumbnail access access_type\n",
       "achapkowski_geosaurus       False     False   True         org\n",
       "amani_geosaurus              True      True   True      public\n",
       "andrew57                     True      True   True     private\n",
       "api_data_owner              False     False   True         org\n",
       "arcgis_python                True      True   True      public"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "user_profile_df = pd.DataFrame(data=user_profile_status, index=complete_profile).T\n",
    "user_profile_df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This dataframe can then be written to a _.csv._ file on your fileshare,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "output_dir = \"/arcgis/home/\"\n",
    "current = str(int(dt.datetime.now().timestamp()))\n",
    "out_file = \"output_org_user_profile\" + \"_\" + current + \".csv\"\n",
    "\n",
    "user_profile_df.to_csv(os.path.join(output_dir, out_file), index_label='username')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "and the dataframe can be written to a _.csv_ file item on your Organization."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div class=\"item_container\" style=\"height: auto; overflow: hidden; border: 1px solid #cfcfcf; border-radius: 2px; background: #f6fafa; line-height: 1.21429em; padding: 10px;\">\n",
       "                    <div class=\"item_left\" style=\"width: 210px; float: left;\">\n",
       "                       <a href='https://geosaurus.maps.arcgis.com/home/item.html?id=9f077933498c41fbb222ee95fee23102' target='_blank'>\n",
       "                        <img src='http://static.arcgis.com/images/desktopapp.png' class=\"itemThumbnail\">\n",
       "                       </a>\n",
       "                    </div>\n",
       "\n",
       "                    <div class=\"item_right\"     style=\"float: none; width: auto; overflow: hidden;\">\n",
       "                        <a href='https://geosaurus.maps.arcgis.com/home/item.html?id=9f077933498c41fbb222ee95fee23102' target='_blank'><b>output_org_user_profile_1686349431</b>\n",
       "                        </a>\n",
       "                        <br/><img src='https://geosaurus.maps.arcgis.com/home/js/jsapi/esri/css/images/item_type_icons/layers16.png' style=\"vertical-align:middle;\">CSV by MMajumdar_geosaurus\n",
       "                        <br/>Last Modified: June 09, 2023\n",
       "                        <br/>0 comments, 0 views\n",
       "                    </div>\n",
       "                </div>\n",
       "                "
      ],
      "text/plain": [
       "<Item title:\"output_org_user_profile_1686349431\" type:CSV owner:MMajumdar_geosaurus>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "item_props = ItemProperties(\n",
    "    title = out_file,\n",
    "    item_type = ItemTypeEnum.CSV,\n",
    "    tags = \"user_profile_report\",\n",
    "    snippet = \"Report on user profile data from Python API\"\n",
    ")\n",
    "\n",
    "root_folder = gis.content.folders.get()\n",
    "\n",
    "item = root_folder.add(\n",
    "    item_properties = item_props,\n",
    "    file= os.path.join(output_dir + out_file)\n",
    ").result()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You may download this item if you wish, and if you decide to delete this item after having used it, you may run the script below by updating the `item_id` with the id of this file in your organization."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "item = gis.content.get(item.id)\n",
    "item.delete()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Conclusion\n",
    "\n",
    "This notebook checked attribute values for an organization's users and wrote the results to a _.csv_ file. This file can then be analyzed to validate that all user profiles contain the minimum required attributes as defined by any policies or regulations."
   ]
  }
 ],
 "metadata": {
  "esriNotebookRuntime": {
   "notebookRuntimeName": "ArcGIS Notebook Python 3 Standard",
   "notebookRuntimeVersion": "7.0"
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.11"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": true,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": true,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
